// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#pragma once

#include <memory>
#include <set>
#include <vector>

#include "coin2/exchange/base/order/processor.h"
#include "coin2/strategy/order_executor/util.h"
#include "coin2/strategy/order_executor/working_order_state.h"

namespace coin2::strategy::order_executor {

class CancelExecutor {
 public:
  using IOrderSubsystem = exchange::base::order::IOrderSubsystem;
  using OrderSide = coin::proto::OrderSide;
  using OrderList = WorkingOrderState::OrderList;
  using OrderContext = WorkingOrderState::OrderContext;
  using OrderIdList = std::set<int64_t>;

 public:
  CancelExecutor(WorkingOrderState* wo, bool need_exchange_id)
      : wo_(wo), need_exchange_id_(need_exchange_id) {
    cooldown_limiter_.reset(new CoolDownLimiter(1'000'000'000LL));
  }
  CancelExecutor() = default;
  ~CancelExecutor() = default;

  void AddOrderSubsystem(IOrderSubsystem* os) { os_list_.push_back(os); }

  void Clear();

  bool HasBuyCancel() { return !cancel_buy_list_.empty(); }

  bool HasSellCancel() { return !cancel_sell_list_.empty(); }

  bool HasCancel() { return HasBuyCancel() || HasSellCancel(); }

  void RemoveDuplicates();

  auto& GetCancelBuyList() { return cancel_buy_list_; }

  auto& GetCancelSellList() { return cancel_sell_list_; }

  void CancelBuyWorkingOrders(int64_t timestamp, int64_t exclude_order_id = -1);
  void CancelSellWorkingOrders(int64_t timestamp, int64_t exclude_order_id = -1);
  void CancelBuyWithRisk(
      int64_t timestamp,
      double pos,
      double max_pos,
      double max_working_qty,
      int64_t exclude_order_id = -1);
  void CancelSellWithRisk(
      int64_t timestamp,
      double pos,
      double max_pos,
      double max_working_qty,
      int64_t exclude_order_id = -1);
  void CancelBuyByPrice(
      int64_t timestamp,
      double pull_price,
      double push_price,
      int64_t exclude_order_id = -1);
  void CancelSellByPrice(
      int64_t timestamp,
      double pull_price,
      double push_price,
      int64_t exclude_order_id = -1);
  void CancelOldOrders(int64_t timestamp, int64_t timestamp_min);
  void CancelOldOrdersExceptTag(int64_t timestamp, int64_t timestamp_min, std::string tag);
  void CancelSmallBuyOrders(int64_t timestamp, double qty);
  void CancelSmallSellOrders(int64_t timestamp, double qty);
  const OrderContext* ExtractSell();
  const OrderContext* ExtractBuy();
  void
  CancelOutsideTimeRangeOrders(int64_t timestamp, int64_t timestamp_from, int64_t timestamp_to);

 private:
  std::vector<IOrderSubsystem*> os_list_;
  WorkingOrderState* wo_;
  std::unique_ptr<CoolDownLimiter> cooldown_limiter_;
  OrderIdList cancel_buy_list_;
  OrderIdList cancel_sell_list_;
  bool need_exchange_id_;
};
}  // namespace coin2::strategy::order_executor
